#ifndef ESMD_YAML_MAP_COMMON
#define ESMD_YAML_MAP_COMMON
#include <yaml.h>
#include <stdlib.h>
#include "memory.h"
typedef struct {
  yaml_parser_t parser;
  yaml_event_t event;
  yaml_event_t key_event;
  FILE *input;
} yaml_context_t;

void get_event(yaml_context_t *ctx, int event_type) {
  if (!yaml_parser_parse(&ctx->parser, &ctx->event))
    exit(1);
  //printf("%d\n", ctx->event.type);
  if (ctx->event.type != event_type)
    exit(1);
}

yaml_context_t *yaml_open(const char *path) {
  yaml_context_t *esmd_imalloc1(ctx, "yaml/context");
  yaml_parser_initialize(&ctx->parser);
  ctx->input = fopen(path, "rb");
  yaml_parser_set_input_file(&ctx->parser, ctx->input);
  get_event(ctx, YAML_STREAM_START_EVENT);
  yaml_event_delete(&ctx->event);
  get_event(ctx, YAML_DOCUMENT_START_EVENT);
  yaml_event_delete(&ctx->event);
  return ctx;
}

yaml_context_t *yaml_close(yaml_context_t *ctx) {
  yaml_parser_delete(&ctx->parser);
  yaml_event_delete(&ctx->event);
}

int key_event(yaml_context_t *ctx) {
  if (!yaml_parser_parse(&ctx->parser, &ctx->key_event))
    exit(1);
  //printf("%d\n", ctx->event.type);
  return ctx->key_event.type;
}

int next_event(yaml_context_t *ctx) {
  if (!yaml_parser_parse(&ctx->parser, &ctx->event))
    exit(1);
  //printf("%d\n", ctx->event.type);
  return ctx->event.type;
}
char *get_scalar(yaml_context_t *ctx) {
  get_event(ctx, YAML_SCALAR_EVENT);
  return (char *)ctx->event.data.scalar.value;
}

void pop_any(yaml_context_t *ctx) {
  int level = 0;
  do {
    int event = next_event(ctx);
    switch (event) {
    case YAML_MAPPING_START_EVENT:
      level++;
      break;
    case YAML_MAPPING_END_EVENT:
      level--;
      break;
    case YAML_SEQUENCE_START_EVENT:
      level++;
      break;
    case YAML_SEQUENCE_END_EVENT:
      level--;
    }
    yaml_event_delete(&ctx->event);
  } while (level > 0);
}
char *pop_str(yaml_context_t *ctx) {
  char *value = get_scalar(ctx);
  size_t len = strlen(value);
  char *esmd_imalloc(ret, len + 1, "yaml/string");
  strcpy(ret, value);
  yaml_event_delete(&ctx->event);
  return ret;
}

char *pop_key(yaml_context_t *ctx) {
  int event_type = key_event(ctx);
  if (event_type == YAML_MAPPING_END_EVENT)
    return NULL;
  else if (event_type == YAML_SCALAR_EVENT)
    return (char *)ctx->key_event.data.scalar.value;
  else
    exit(1);
}
int pop_int(yaml_context_t *ctx) {
  int ret = atoi(get_scalar(ctx));
  yaml_event_delete(&ctx->event);
  return ret;
}

double pop_float(yaml_context_t *ctx) {
  double ret = atof(get_scalar(ctx));
  yaml_event_delete(&ctx->event);
  return ret;
}

void pop_ivec(int *ret, yaml_context_t *ctx) {
  get_event(ctx, YAML_SEQUENCE_START_EVENT);
  yaml_event_delete(&ctx->event);
  for (int i = 0; i < 3; i++)
    ret[i] = pop_int(ctx);
  get_event(ctx, YAML_SEQUENCE_END_EVENT);
  yaml_event_delete(&ctx->event);
}

void pop_rvec(double *ret, yaml_context_t *ctx) {
  get_event(ctx, YAML_SEQUENCE_START_EVENT);
  yaml_event_delete(&ctx->event);
  for (int i = 0; i < 3; i++)
    ret[i] = pop_float(ctx);
  get_event(ctx, YAML_SEQUENCE_END_EVENT);
  yaml_event_delete(&ctx->event);
}
#endif
#ifndef YAML_ROOT
#error "YAML_ROOT not defined, nothing will be generated"
#endif
#define YAML_MAPPING(name, children) \
  struct {                           \
    children;                        \
  } name

#define YAML_ROOT_MAPPING(name, children) \
  struct name {                           \
    children;                             \
  }
#define YAML_STR(name) char *name
#define YAML_REAL(name) double name
#define YAML_RVEC(name) double name[3]
#define YAML_INT(name) int name
#define YAML_IVEC(name) int name[3]

YAML_ROOT

#undef YAML_MAPPING
#undef YAML_ROOT_MAPPING
#undef YAML_STR
#undef YAML_REAL
#undef YAML_RVEC
#undef YAML_INT
#undef YAML_IVEC

#define YAML_STR(name)          \
  else if (!strcmp(key, #name)) \
      cursor->name = pop_str(ctx)
#define YAML_INT(name)          \
  else if (!strcmp(key, #name)) \
      cursor->name = pop_int(ctx)
#define YAML_REAL(name)         \
  else if (!strcmp(key, #name)) \
      cursor->name = pop_float(ctx)

#define YAML_IVEC(name)         \
  else if (!strcmp(key, #name)) \
      pop_ivec(cursor->name, ctx)

#define YAML_RVEC(name)         \
  else if (!strcmp(key, #name)) \
      pop_rvec(cursor->name, ctx)

#define YAML_MAPPING(name, children)                \
  else if (!strcmp(key, #name))({                   \
    typeof(cursor) parent = cursor;                 \
    get_event(ctx, YAML_MAPPING_START_EVENT);       \
    yaml_event_delete(&ctx->event);                 \
    {                                               \
      typeof(parent->name) *cursor = &parent->name; \
      while (1) {                                   \
        key = pop_key(ctx);                         \
        if (!key)                                   \
          break;                                    \
        children;                                   \
        else pop_any(ctx);                          \
        yaml_event_delete(&ctx->key_event);         \
      }                                             \
    }                                               \
  })

#define YAML_ROOT_MAPPING(name, children)                       \
  void parse_##name(struct name *cursor, yaml_context_t *ctx) { \
    char *key;                                                  \
    typeof(cursor) parent = cursor;                             \
    get_event(ctx, YAML_MAPPING_START_EVENT);                   \
    yaml_event_delete(&ctx->event);                             \
    {                                                           \
      while (1) {                                               \
        key = pop_key(ctx);                                     \
        if (!key)                                               \
          break;                                                \
        children;                                               \
        else pop_any(ctx);                                      \
        yaml_event_delete(&ctx->key_event);                     \
      }                                                         \
    }                                                           \
  }

YAML_ROOT

#undef YAML_MAPPING
#undef YAML_ROOT_MAPPING
#undef YAML_STR
#undef YAML_REAL
#undef YAML_RVEC
#undef YAML_INT
#undef YAML_IVEC